// Logger.h	class Logger

#ifndef _KC_LOGGER__H_2008_KC_
#define _KC_LOGGER__H_2008_KC_

#include <iostream>
#include <string>
#include <boost/shared_ptr.hpp>
#include "LogStream.h"

/** 
 * Każda klasa ma własny obiekt logów Logger. Wszystkie loggery zapisują 
 * zdarzenia do jednego pliku logów.
 * Wpisy są dodawane do pliku logów z kontrolą dostępu.
 * Pojedynczy wpis w logu ma treść: 
 * data - godzina - wpis.
 */
  
namespace kc_sf
{

	extern const std::string DEF_LOG_FILE;

	enum LogLevel
	{
		Nothing = 0,
		Fatal,
		Error,
		Info,
		Detail = 4,
		Debug,
		All,
		LevelsNr
	};

	/**
	 * Poziomy dokładności logów:
	 *
	 * NOTHING  - zapisywanie informacji do logów wyłączone
	 * FATAL    - system fatal errors; zadanie nie może być kontynuowane
	 *            oraz system ( serwer ) musi zakończyć działanie;
	 *            np. memory access violation
	 * ERROR    - zadanie nie może być kontynuowane, ale system działa dalej
	 *            np. timeout przy próbie połącznia z serwerem smtp, brak
	 *            pamięci
	 * INFO     - ogólne informacje o zadaniu, np. odebrano wiadomość
	 * DETAIL   - bardziej szczegółowe informacje nt. przetwarzania
	 *            wiadomości, np. odnaleziono następujące błędy w
	 *            wiadomości: ....
	 * ALL      - to samo co DETAIL ( czyli wszystkie powyższe )
	 * DEBUG    - informacje do debuggowania systemu
	 *
	 * Poziomy logów, które są poniżej, zawierają w
	 * sobie informacje z tych, które zapisano nad nimi.
	 */
	// każdy ma własny obiekt logów, ale plik jest wspólny
	class Logger;
	typedef Logger & (*ManipPtr)( Logger & );
	typedef std::ostream & (*OstreamManipPtr)( std::ostream& os );

	class Logger
	{
		public:

			static const LogLevel DEF_LOG_LEVEL = Info;
			static const std::string DEF_LOG_LEVEL_NAME;

			static const std::string LogLevelNames[];

			// metody
			explicit Logger();
			explicit Logger( const std::string &accepted_log_level_name );

			explicit Logger( const std::string &log_filename,
							 bool append = false );			                // czy dopisywać do pliku, czy nadpisać jego zawartość

			explicit Logger( const std::string &log_filename,
							 bool append = false, 	                  		// czy dopisywać do pliku, czy nadpisać jego zawartość
							 const std::string &accepted_log_level_name
							 = DEF_LOG_LEVEL_NAME );

			explicit Logger( LogStream &stream );

			Logger( LogStream &stream,
					const std::string &accepted_log_level_name );

			LogStream & getStream();
			const LogStream & getStream() const;

			bool open( bool append = true );
			bool isOpened() const;
			void close();

			void setMsgPriority( LogLevel log_priority );       // ustawia priorytet dla zapisywanych po jego wywołaniu logów, aż do odwołania

			void write( LogLevel log_level, const std::string &log_msg );  // zapis do logu informacji o priorytecie event_type
			void write( LogLevel log_level, int nr );           // zapis do logu informacji o priorytecie event_type

			void write( const std::string &log_msg );           // zapis do logu, priorytet odczytuje z l_log_level
			void write( int nr );                               // zapis do logu, priorytet odczytuje z l_log_level
			void writeNewLine();                                // dodaje znak nowej linii i wypisuje odpowiednia ilosc spacji w nowej

			// przyjaciele klasy
			friend Logger & flush( Logger & logger );
			friend Logger & nothing( Logger & logger );
			friend Logger & fatal( Logger & logger );
			friend Logger & error( Logger & logger );
			friend Logger & info( Logger & logger );
			friend Logger & detail( Logger & logger );
			friend Logger & debug( Logger & logger );
			friend Logger & all( Logger & logger );
			friend Logger & newLine( Logger & logger );

		public:

			// operator <<
			Logger & operator<<( const std::string &log_msg );  // zapis do logu, priorytet odczytuje z l_log_level
			Logger & operator<<( int nr );                      // zapis do logu, priorytet odczytuje z l_log_level
			Logger & operator<<( ManipPtr manip );              // obsługa manipulatorów

			static LogLevel
			mapLevelNameToInt( const std::string &log_level_name );

		private:

			LogStream       &l_stream;          // uchwyt do klasy synchronizującej dostęp do pliku logów
			LogLevel        l_accepted_level;   // poziom dokładności logu
			LogLevel        l_log_level;        // priorytet aktualnego podzadania - sprawdzany z poziomem l_accepted_level, jak nizszy - odrzucany
			std::string     l_cache;            // buforuje napis, az nie zostanie wymuszone jego zapisanie w pliku
			bool            l_initiated;        // dodaje do wpisu znaczek czasu

			// prywatne konstruktory; same nagłówki - kopiowanie nie dozwolone
			Logger( const Logger & );
			Logger & operator=( const Logger & );
	};

	typedef boost::shared_ptr<Logger > LoggerPtr;
	typedef boost::shared_ptr<const Logger > LoggerConstPtr;

	// manipulatory
	Logger & flush( Logger & logger );           // wymusza zapis informacji z bufora tymczasowego do pliku
	Logger & nothing( Logger & logger );         // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na NOTHING
	Logger & fatal( Logger & logger );           // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na FATAL
	Logger & error( Logger & logger );           // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na ERRO
	Logger & info( Logger & logger );            // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na INFO
	Logger & detail( Logger & logger );          // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na DETAIL
	Logger & debug( Logger & logger );           // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na DEBUG
	Logger & all( Logger & logger );             // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na ALL
	Logger & newLine( Logger & logger );             // ustawia priorytet tego, co zostanie zapisane po nim aż do nowej linii na ALL

	Logger & watchdog( Logger & logger );
	Logger & listener( Logger & logger );
	Logger & receiver( Logger & logger );
	Logger & sender( Logger & logger );
	Logger & analyzer( Logger & logger );
	Logger & archiver( Logger & logger );

	Logger & messageId( Logger & logger, unsigned long message_id );
	Logger & connectionInId( Logger & logger, unsigned long connection_id );
	Logger & connectionOutId( Logger & logger, unsigned long connection_id );

	// manipulatory z argumentami
	enum IdType { Mid, CidIn, CidOut };

	class LogId
	{
		public:

			LogId( IdType id_type, unsigned long id )
			: l_id_type( id_type ),
			  l_id( id )
			{
			}

			void operator()( Logger &logger ) const
			{
				l_id_type == Mid ? messageId( logger, l_id )
						: l_id_type == CidIn ? connectionInId( logger, l_id )
						: connectionOutId( logger, l_id );
			}

		private:

			IdType l_id_type;
			unsigned long l_id;
	};

	LogId mid( unsigned long id );
	LogId cidIn( unsigned long id );
	LogId cidOut( unsigned long id );
	Logger& operator<<( Logger& logger, const LogId & id);
}

#endif // _KC_LOGGER__H_2008_KC_
